﻿using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace STM32F1_BootLoader_GUI
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            InitClockTimer();
            Scan_UART(true);
            Hex_Data_Pack.Memery_Data_len = 0;
            Hex_Data_Pack.Start_Address = 0;
            time_start = (ulong)DateTime.Now.ToFileTime();
        }

        Hex_Info Hex_Data_Pack = new Hex_Info();
        SerialPort UART = new SerialPort();

        /*BootLoader下载命令定义*/
        private const int READ_VERSION = 0xF0;
        private const int ERASE_FLASH = 0xF1;
        private const int WRITE_FLASH = 0xF2;
        private const int CALC_CHECKSUM = 0xF3;
        private const int Jump_APP = 0xF4;
        private const int DownLoad_Faild = 0xFF;


        private UInt32 Fram_Block_Size = 0x400;
        private UInt32 Max_Flash_Address = 0;


        UInt64 time_start = 0;
        UInt64 time = 0;

        /*BootLoader下载标志位及结构体定义*/
        Thread BL_thread;
        DateTime BL_Start_Time;
        DateTime BL_Stop_Time;

        private UInt32 Write_Count = 0;
        private int BL_Current_CMD = READ_VERSION;
        private bool BootLoader_DownLoad_Need = false;

        /*定时器*/
        private DispatcherTimer clockTimer = new DispatcherTimer();
        private void InitClockTimer()
        {
            clockTimer.Interval = new TimeSpan(0, 0, 0, 0, 10);
            clockTimer.IsEnabled = true;
            clockTimer.Tick += ClockTimer_Tick;
            clockTimer.Start();
        }

        private void ClockTimer_Tick(object sender, EventArgs e)
        {
            if (BootLoader_DownLoad_Need != true)
            {
                return;
            }
            switch (BL_Current_CMD)
            {
                case READ_VERSION:
                    {
                        BL_Display_Message("READ VERSION...");
                        break;
                    }
                case ERASE_FLASH:
                    {
                        BL_Start_Time = System.DateTime.Now;
                        BL_Display_Message("ERASE FLASH...");
                        break;
                    }
                case WRITE_FLASH:
                    {
                        if (Write_Count > 0)
                        {
                            BL_Display_Message("Write Flash Success!");
                            BL_Display_Message("Write Next Pack: [" + (Write_Count / 0x400).ToString() + "]");
                        }
                        else
                        {
                            BL_Display_Message("Start Write Flash!");
                        }
                        break;
                    }
                case CALC_CHECKSUM:
                    {
                        BL_Display_Message("Data write complet!");
                        break;
                    }
                case Jump_APP:
                    {
                        BL_Display_Message("Program verification succeeded!");
                        BL_Display_Message("Update Success!");
                        BL_DownLoad.Content = "DownLoad";
                        BL_Stop_Time = System.DateTime.Now;
                        var BL_Elapsed_Time = BL_Stop_Time.Subtract(BL_Start_Time).TotalSeconds;
                        BL_Display_Message("Elapsed Time: " + BL_Elapsed_Time.ToString() + " s");
                        BootLoader_DownLoad_Need = false;
                        BL_ProgressBar_Left.Value = 100;
                        BL_ProgressBar_Right.Value = 100;
                        Write_Count = 0;
                        BL_Display_Message("Jump to user program！");
                        BL_Display_Message("BootLoader Success!!");
                        BL_thread.Abort();
                        Enable_Entry_BL();
                        break;
                    }
                default:
                    {
                        break;
                    }
            }
            Frush_ProgressBar();
            BackgroundUpdate();
        }

        private void BackgroundUpdate()
        {
            SolidColorBrush ConsoleBrush = new SolidColorBrush(Color.FromArgb(0xFF, (byte)(Write_Count & 0xFF), (byte)((Write_Count >> 8) & 0xFF), (byte)((Write_Count >> 16) & 0xFF)));
            ConsoleBrush.Opacity = 0.1;
            Log_Console.Background = ConsoleBrush;
        }

        private void Frush_ProgressBar()
        {
            if (Write_Count > 0)
            {
                BL_ProgressBar_Left.Value = Write_Count * 100.0f / Hex_Data_Pack.Memery_Data_len;
                BL_ProgressBar_Right.Value = Write_Count * 100.0f / Hex_Data_Pack.Memery_Data_len;
            }
        }

        void BL_Display_Message(string str)
        {
            Log_Console.AppendText(DateTime.Now.ToLongTimeString() + ":  " + str + Environment.NewLine);
            Log_Console.ScrollToEnd();
        }


        private void Set_BootLoader_Console_Fomat()
        {
            Log_Console.HorizontalContentAlignment = 0;
            Log_Console.VerticalContentAlignment = 0;
            Log_Console.FontSize = 20;
        }

        private void BL_File_Select_Click(object sender, RoutedEventArgs e)
        {
            Set_BootLoader_Console_Fomat();
            Microsoft.Win32.OpenFileDialog aDialog = new Microsoft.Win32.OpenFileDialog();
            aDialog.InitialDirectory = "";
            aDialog.Filter = "hex files (*.hex)|*.hex";
            aDialog.CheckFileExists = true;
            aDialog.CheckPathExists = true;
            aDialog.RestoreDirectory = true;
            if (aDialog.ShowDialog() == true)
            {
                /*打印提示信息*/
                BL_Display_Message("Selected File:" + aDialog.FileName);
                /*打印提示信息*/
                BL_Display_Message("HEX File Opened");
                /*打印提示信息*/
                BL_Display_Message("File Parsing ......");
                /*调用解析函数解析HEX*/
                Hex_Data_Pack = IntelHexParser.Hex_Parser(aDialog.FileName);
                //Hex_Data_Pack = Hex_Info_Cut(Hex_Data_Pack);
                if (Hex_Data_Pack.Memery_Data_len > 0)
                {
                    /*打印提示信息*/
                    BL_Display_Message("Hex File Parsed Successfully");
                    BL_Display_Message("Start Address: 0x" + Hex_Data_Pack.Start_Address.ToString("X"));
                    BL_Display_Message("Hex Valid Data Size: 0x" + Hex_Data_Pack.Valid_Data_len.ToString("X"));
                    BL_Display_Message("Hex Memery Data Size: 0x" + Hex_Data_Pack.Memery_Data_len.ToString("X"));
                }
                Log_Console.ScrollToEnd();
            }
        }

        private void BL_Entrt_BL_Click(object sender, RoutedEventArgs e)
        {
            if (UART.IsOpen == false)
            {
                BL_Display_Message("Please open the serial port！");
                return;
            }
            else
            {
                UART.Close();
                UART.BaudRate = 115200;
                UART.Open();
                UART.DiscardInBuffer();
                UART.Write(new byte[] { 0xFA, 0xAF, 0x55, 0xAA }, 0, 4);
                Thread.Sleep(20);
                if (UART.BytesToRead != 0)
                {
                    byte[] buffer = new byte[10];
                    UART.Read(buffer, 0, UART.BytesToRead);
                    if ((buffer[0] == 0xAA)
                        && (buffer[1] == 0x55)
                        && (buffer[2] == 0xAF)
                         && (buffer[3] == 0xFA))
                    {
                        BL_Display_Message("Successfully enter Boot Load!!");
                    }
                }
                else
                {
                    BL_Display_Message("Failed to enter Boot Load, or Boot Load is already running！");
                }
                UART.Close();
                UART.BaudRate = 1000000;
                UART.Open();

            }
        }

        private void BL_DownLoad_Click(object sender, RoutedEventArgs e)
        {
            if (UART.IsOpen != true)
            {
                BL_Display_Message("Please Open the Serial port！");
                return;
            }

            /*判断是否选中了正确的HEX文件*/
            if (((Hex_Data_Pack.Start_Address & 0x08000000) != 0x08000000)
                || (Hex_Data_Pack.Memery_Data_len == 0))
            {
                BL_Display_Message("Please select the correct HEX file！");
                return;
            }

            if (BL_thread != null)
            { 
                BL_thread.Abort();
            }

            if (BootLoader_DownLoad_Need == true)
            {
                Log_Console.Clear();
                BL_ProgressBar_Left.Value = 100;
                BL_ProgressBar_Right.Value = 100;
                if (BL_thread != null)
                {
                    BL_thread.Abort();
                }
                BL_DownLoad.Content = "DownLoad";
                BootLoader_DownLoad_Need = false;
                Enable_Entry_BL();
            }
            else
            {
                Log_Console.Clear();
                BL_ProgressBar_Left.Value = 0;
                BL_ProgressBar_Right.Value = 0;
                Write_Count = 0;
                BL_Current_CMD = READ_VERSION;
                BootLoader_DownLoad_Need = true;
                BL_thread = new Thread(BL_Data_Receive_Deal);
                BL_thread.Start();
                BL_DownLoad.Content = "Stop";
                Disable_Entry_BL();
            }

        }

        private void Enable_Entry_BL()
        {
            BL_Entrt_BL.IsEnabled = true;
            BL_Entrt_BL.Foreground = new SolidColorBrush(Color.FromArgb(0xFF,0xFF,0xFF,0xFF));
        }

        private void Disable_Entry_BL()
        {
            BL_Entrt_BL.IsEnabled = false;
            BL_Entrt_BL.Foreground = new SolidColorBrush(Color.FromArgb(0xFF, 0, 0, 0));
        }

        private bool BL_Send_Data(int CMD_Index)
        {
            switch (CMD_Index)
            {
                case (READ_VERSION):
                    {
                        byte[] Send_Array = new byte[9];
                        Send_Array[0] = READ_VERSION;
                        Send_Array[1] = 0;
                        Send_Array[2] = 0;
                        Send_Array[3] = 0;
                        Send_Array[4] = 0;
                        Send_Array[5] = 0;
                        Send_Array[6] = 0;
                        Send_Array[7] = 0;
                        Send_Array[8] = 0;
                        UART.Write(Send_Array, 0, Send_Array.Length);
                        break;
                    }
                case (ERASE_FLASH):
                    {
                        UInt32 Start_Address = Hex_Data_Pack.Start_Address;
                        byte[] Send_Array = new byte[9];
                        UInt32 Erase_Length = (UInt32)(Max_Flash_Address+1 - Start_Address);
                        Send_Array[0] = ERASE_FLASH;
                        Send_Array[1] = (byte)(Erase_Length & 0xFF);
                        Send_Array[2] = (byte)((Erase_Length >> 8) & 0xFF);
                        Send_Array[3] = (byte)((Erase_Length >> 16) & 0xFF);
                        Send_Array[4] = (byte)((Erase_Length >> 24) & 0xFF);
                        Send_Array[5] = (byte)(Start_Address & 0xFF);
                        Send_Array[6] = (byte)((Start_Address >> 8) & 0xFF);
                        Send_Array[7] = (byte)((Start_Address >> 16) & 0xFF);
                        Send_Array[8] = (byte)((Start_Address >> 24) & 0xFF);
                        UART.Write(Send_Array, 0, Send_Array.Length);
                        break;
                    }
                case (WRITE_FLASH):
                    {
                        UInt32 Remaining_bytes = Hex_Data_Pack.Memery_Data_len - Write_Count;
                        if (Remaining_bytes > Fram_Block_Size)
                            Remaining_bytes = Fram_Block_Size;
                        UInt32 Write_Address = Hex_Data_Pack.Start_Address + Write_Count;
                        byte[] Send_Array = new byte[9 + Remaining_bytes];
                        Send_Array[0] = WRITE_FLASH;
                        Send_Array[1] = (byte)(Remaining_bytes & 0xFF);
                        Send_Array[2] = (byte)((Remaining_bytes >> 8) & 0xFF);
                        Send_Array[3] = (byte)((Remaining_bytes >> 16) & 0xFF);
                        Send_Array[4] = (byte)((Remaining_bytes >> 24) & 0xFF);
                        Send_Array[5] = (byte)(Write_Address & 0xFF);
                        Send_Array[6] = (byte)((Write_Address >> 8) & 0xFF);
                        Send_Array[7] = (byte)((Write_Address >> 16) & 0xFF);
                        Send_Array[8] = (byte)((Write_Address >> 24) & 0xFF);
                        for (UInt32 i = 0; i < Remaining_bytes; i++)
                        {
                            Send_Array[9 + i] = Hex_Data_Pack.Data[Write_Count + i];
                        }
                        UART.Write(Send_Array, 0, Send_Array.Length);
                        Write_Count += Remaining_bytes;
                        break;
                    }
                case (CALC_CHECKSUM):
                    {
                        UInt32 Start_Address = Hex_Data_Pack.Start_Address;
                        byte[] Send_Array = new byte[9];
                        UInt32 Check_Length = Hex_Data_Pack.Memery_Data_len;
                        Send_Array[0] = CALC_CHECKSUM;
                        Send_Array[1] = (byte)(Check_Length & 0xFF);
                        Send_Array[2] = (byte)((Check_Length >> 8) & 0xFF);
                        Send_Array[3] = (byte)((Check_Length >> 16) & 0xFF);
                        Send_Array[4] = (byte)((Check_Length >> 24) & 0xFF);
                        Send_Array[5] = (byte)(Start_Address & 0xFF);
                        Send_Array[6] = (byte)((Start_Address >> 8) & 0xFF);
                        Send_Array[7] = (byte)((Start_Address >> 16) & 0xFF);
                        Send_Array[8] = (byte)((Start_Address >> 24) & 0xFF);
                        UART.Write(Send_Array, 0, Send_Array.Length);
                        break;
                    }
                case (Jump_APP):
                    {
                        UInt32 Start_Address = Hex_Data_Pack.Start_Address;
                        byte[] Send_Array = new byte[9];
                        UInt32 Check_Length = Hex_Data_Pack.Memery_Data_len;
                        Send_Array[0] = Jump_APP;
                        Send_Array[1] = (byte)(Check_Length & 0xFF);
                        Send_Array[2] = (byte)((Check_Length >> 8) & 0xFF);
                        Send_Array[3] = (byte)((Check_Length >> 16) & 0xFF);
                        Send_Array[4] = (byte)((Check_Length >> 24) & 0xFF);
                        Send_Array[5] = (byte)(Start_Address & 0xFF);
                        Send_Array[6] = (byte)((Start_Address >> 8) & 0xFF);
                        Send_Array[7] = (byte)((Start_Address >> 16) & 0xFF);
                        Send_Array[8] = (byte)((Start_Address >> 24) & 0xFF);
                        UART.Write(Send_Array, 0, Send_Array.Length);
                        break;
                    }
                default:
                    {
                        break;
                    }
            }
            return true;
        }


        byte[] UART_Buffer = new byte[20];
        private void BL_Data_Receive_Deal()
        {
            BL_Send_Data(READ_VERSION);
            Thread.Sleep(10);
            while (BootLoader_DownLoad_Need == true)
            {
                while (UART.BytesToRead == 0) ;
                UART.Read(UART_Buffer, 0, 1);
                if (UART_Buffer[0] != 0xff)
                {
                    switch (UART_Buffer[0])
                    {
                        case (READ_VERSION):
                            {
                                while (UART.BytesToRead < 14) ;
                                UART.Read(UART_Buffer, 1, 14);
                                if ((UART_Buffer[9] == 1) && (UART_Buffer[10] == 0))
                                {
                                    Max_Flash_Address = ((uint)((UART_Buffer[14] << 24) | (UART_Buffer[13] << 16) | (UART_Buffer[12] << 8) | UART_Buffer[11]));
                                    BL_Send_Data(ERASE_FLASH);
                                    BL_Current_CMD = ERASE_FLASH;
                                }
                                else
                                {
                                    BL_Current_CMD = DownLoad_Faild;
                                    BootLoader_DownLoad_Need = false;
                                    return;
                                }
                                UART.DiscardInBuffer();
                                break;
                            }
                        case (ERASE_FLASH):
                            {
                                while (UART.BytesToRead < 9) ;
                                UART.Read(UART_Buffer, 1, 9);
                                if (UART_Buffer[9] == 1)
                                {
                                    BL_Send_Data(WRITE_FLASH);
                                    BL_Current_CMD = WRITE_FLASH;
                                }
                                else
                                {
                                    BL_Current_CMD = DownLoad_Faild;
                                    BootLoader_DownLoad_Need = false;
                                    return;
                                }
                                UART.DiscardInBuffer();
                                break;
                            }
                        case (WRITE_FLASH):
                            {
                                while (UART.BytesToRead < 9) ;
                                UART.Read(UART_Buffer, 1, 9);
                                if (UART_Buffer[9] == 1)
                                {
                                    if (Write_Count == Hex_Data_Pack.Memery_Data_len)
                                    {
                                        BL_Send_Data(CALC_CHECKSUM);
                                        BL_Current_CMD = CALC_CHECKSUM;
                                    }
                                    else
                                    {
                                        BL_Send_Data(WRITE_FLASH);
                                        BL_Current_CMD = WRITE_FLASH;
                                    }
                                }
                                else
                                {
                                    BL_Current_CMD = DownLoad_Faild;
                                    BootLoader_DownLoad_Need = false;
                                    return;
                                }
                                UART.DiscardInBuffer();
                                break;
                            }
                        case (CALC_CHECKSUM):
                            {
                                while (UART.BytesToRead < 9) ;
                                UART.Read(UART_Buffer, 1, 9);
                                if (UART_Buffer[9] == Hex_Data_Pack.CheckSum)
                                {
                                    BL_Send_Data(Jump_APP);
                                    BL_Current_CMD = Jump_APP;
                                }
                                else
                                {
                                    BL_Current_CMD = DownLoad_Faild;
                                    BootLoader_DownLoad_Need = false;
                                    return;
                                }
                                UART.DiscardInBuffer();
                                break;
                            }
                        default:
                            {
                                break;
                            }
                    }
                }
            }
        }

        private void Scan_UART(bool print)
        {
            var ports = SerialPort.GetPortNames();
            if (ports.Length > 0)
            {
                BL_UART_LIST.Items.Clear();
                for (int i = 0; i < ports.Length; i++)
                {
                    BL_UART_LIST.Items.Add(ports[i]);
                }
                if (print)
                { 
                    BL_Display_Message(string.Format("{0} serial ports found！", ports.Length));
                }
                BL_UART_LIST.SelectedIndex = 0;
                BL_UART_LIST.Text = ports[0];
            }
            else
            {
                if (print)
                {
                    BL_Display_Message("No serial port available!");
                }
            }
        }

        private void BL_Connect_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                if (UART.IsOpen)
                {
                    BL_Connect.Content = "Connect";
                    UART.Close();
                    BL_UART_LIST.IsEnabled = true;
                    Enable_Entry_BL();
                    Scan_UART(false);
                }
                else
                {
                    UART.PortName = BL_UART_LIST.Text;
                    UART.BaudRate = 1000000;
                    UART.Parity = Parity.None;
                    UART.DataBits = 8;
                    UART.StopBits = StopBits.One;
                    UART.Open();
                    UART.DiscardInBuffer();
                    UART.DiscardOutBuffer();
                    BL_Display_Message(UART.PortName+" Opend!");
                    BL_Connect.Content = "Disconnect";
                    BL_UART_LIST.IsEnabled = false;
                }
            }
            catch
            {
                BL_Connect.Content = "Connect";
                UART.Close();
                BL_UART_LIST.IsEnabled = true;
                Enable_Entry_BL();
                Scan_UART(false);
            }
        }
    }
}
